﻿using Microsoft.Win32;
using SecurityApp.Classes;
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace SecurityApp
{

    public partial class SteganoWindow : Window
    {
        static int intTijd;
        WriteableBitmap bmVerschil;
        static Boolean isDecryptie, isCorrectVerlopen, isAborted;
        OpenFileDialog ofdOpenen = new OpenFileDialog();
        public BackgroundWorker bwProces = new BackgroundWorker();
        String strUitvoer, strGeheimBestand, strSleutel, strLocatieFoto, strExtentie; 
        static System.Timers.Timer tmrElapsed = new System.Timers.Timer();
        Window detail;

        public SteganoWindow()
        {
            InitializeComponent();
            isAborted = false;

            // Bij elke tick tmr_Tick() uitvoeren om de 100ms
            tmrElapsed.Interval = 100;
            tmrElapsed.Elapsed += new ElapsedEventHandler(tmrElapsed_Tick);

            // BackgroundWorker configureren
            bwProces.WorkerReportsProgress = true;
            bwProces.DoWork += new DoWorkEventHandler(bw_DoWork);
            bwProces.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
        }

        #region ### Blader knoppen ###

        private void btnBladerenFoto_Click(object sender, RoutedEventArgs e)
        {
            txtLocatieFoto.Clear();
            txtOutput.Text = "";
            btnImages.Visibility = Visibility.Hidden;

            // Dialoogvenster openen om een bestand te selecteren + filter instellen
            ofdOpenen.Filter = "BMP bestanden (*.bmp)|*.bmp";
            Boolean dialogResult = (Boolean) ofdOpenen.ShowDialog();

            // Nagaan of er een bestand geselecteerd is
            if (dialogResult)
            {
                txtLocatieFoto.Text = ofdOpenen.FileName;
                strLocatieFoto = ofdOpenen.FileName;
            }
        }

        private void btnBladerenGeheimBestand_Click(object sender, RoutedEventArgs e)
        {
            txtGeheimBestand.Clear();
            txtOutput.Text = "";
            btnImages.Visibility = Visibility.Hidden;

            // Dialoogvenster openen om een bestand te selecteren + filter instellen
            ofdOpenen.Filter = "Alle bestanden (*.)|*.*";
            Boolean dialogResult = (Boolean)ofdOpenen.ShowDialog();

            // Nagaan of er een bestand geselecteerd is
            if (dialogResult)
            {
                txtGeheimBestand.Text = ofdOpenen.FileName;
                strGeheimBestand = ofdOpenen.FileName;
            }
        }

        #endregion

        #region ### Encrypteer & Decrypteer knop ###

        private void btnEncrypteren_Click(object sender, RoutedEventArgs e)
        {
            // Nagaan of de sleutel uit 24 karakters bestaat en of er een bestand geselecteerd is
            if (controleerBestand() && controleerSleutel() && ! Crypto.isWeakKey(txtSleutel.Text))
            {
                disableControls();
                strSleutel = txtSleutel.Text;
                txtOutput.Text = "";
                isDecryptie = false;
                buzzyCircle.Visibility = Visibility.Visible;
                strUitvoer = strLocatieFoto.Replace(strLocatieFoto.Substring(strLocatieFoto.LastIndexOf(".")), "_encryp.bmp");
                strExtentie = strGeheimBestand.Substring(strGeheimBestand.LastIndexOf(".") + 1);
                bwProces.RunWorkerAsync();
            }
            else
            {
                MessageBox.Show("Gelieve elk veld correct in te vullen.\nMogelijk is de sleutel onveilig.", "Foutieve invoer!", MessageBoxButton.OK, MessageBoxImage.Exclamation);
            }
        }

        private void btnDecrypteren_Click(object sender, RoutedEventArgs e)
        {
            // Nagaan of de sleutel uit 24 karakters bestaat en of er een bestand geselecteerd is
            if (controleerBestandEnkel() && controleerSleutel() && ! Crypto.isWeakKey(txtSleutel.Text))
            {
                disableControls();
                strSleutel = txtSleutel.Text;
                txtOutput.Text = "";
                isDecryptie = true;
                buzzyCircle.Visibility = Visibility.Visible;
                strUitvoer = strLocatieFoto.Replace(strLocatieFoto.Substring(strLocatieFoto.LastIndexOf(".")), "_decryp");
                bwProces.RunWorkerAsync();
            }
            else
            {
                MessageBox.Show("Gelieve elk veld correct in te vullen.\nMogelijk is de sleutel onveilig.", "Foutieve invoer!", MessageBoxButton.OK, MessageBoxImage.Exclamation);
            }
        }

        #endregion

        #region ### BackgroundWorker ###

        private void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            // Tijd op 0 zetten en timer starten
            intTijd = 0;
            tmrElapsed.Start();
            isCorrectVerlopen = true;

            // Inlezen van de foto en omzetten naar binair
            String binaireInvoerFoto = FileIO.bestandNaarBinair(strLocatieFoto);

            // Aantal vrije bits berekenen (totale lengte - head / 8 )
            int aantalPlaatsen = (binaireInvoerFoto.Length - 432) / 8;

            if (isDecryptie)
            {
                // Starten Stegano -> Versleuteld geheim bestand uit de foto halen
                String binaireUitvoerBestand = Stegano.startDecryptie(binaireInvoerFoto);

                binaireUitvoerBestand = binaireUitvoerBestand.TrimEnd('0');
                int aantalTekens = (int) Math.Ceiling(binaireUitvoerBestand.Length / 8.0);
                binaireUitvoerBestand = binaireUitvoerBestand.PadRight(aantalTekens * 8, '0');

                // Starten Crypto -> Decrypteren van het versleuteld geheim bestand
                String binaireUitvoer = Crypto.tripleDES_Encrypt(binaireUitvoerBestand, isDecryptie);

                if (binaireUitvoer.Equals("abort"))
                {
                    isAborted = true;
                }
                else
                {
                    // Het bestand wegschrijven
                    FileIO.binairNaarBestand(strUitvoer, binaireUitvoer);
                }
            }
            else
            {
                // Het geheim bestand uitlezen en omzetten naar binair
                String binaireGeheimBestand = FileIO.bestandNaarBinair(strGeheimBestand);

                // Controle of het geheim bestand wel past in de foto
                if (aantalPlaatsen > binaireGeheimBestand.Length)
                {
                    // Starten Crypto -> Encrypteren van het geheim bestand
                    String binaireVersleuteldGeheimBestand = Crypto.tripleDES_Encrypt(binaireGeheimBestand, isDecryptie);

                    // Starten Stegano -> Versleuteld geheim bestand samenvoegen met de foto
                    String binaireUitvoer = Stegano.startEncryptie(binaireVersleuteldGeheimBestand, binaireInvoerFoto);

                    if (binaireUitvoer.Equals("abort"))
                    {
                        isAborted = true;
                    }
                    else
                    {
                        // Het bestand wegschrijven
                        FileIO.binairNaarBestand(strUitvoer, binaireUitvoer);
                    }
                }
                else
                {
                    MessageBox.Show("De foto is te klein om alle gegevens te kunnen versleutelen.", "Ongeldige invoer!", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                    isCorrectVerlopen = false;
                }
            }

            // Proces is afgelopen -> timer stoppen
            tmrElapsed.Stop();
        }

        private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            enableControls();
            buzzyCircle.Visibility = Visibility.Hidden;

            if (isCorrectVerlopen)
            {
                if (isAborted)
                {
                    txtOutput.Inlines.Clear();
                    txtOutput.Inlines.Add(new Run("Het proces is niet voltooid."));
                    txtOutput.Inlines.Add(new Run("\nEr deed zich een onverwachte fout voor."));
                }
                else
                {
                    Double tijd = (intTijd / 10f) == 0 ? 0.1 : (intTijd / 10f);
                    txtOutput.Inlines.Clear();
                    txtOutput.Inlines.Add(new Run("Het proces is voltooid en duurde "));
                    txtOutput.Inlines.Add(new Run(tijd + "") { Foreground = Brushes.DarkBlue });
                    txtOutput.Inlines.Add(new Run(" seconden.\n\nDe uitvoer is opgeslagen onder de volgende naam:\n"));
                    txtOutput.Inlines.Add(new Run(strUitvoer.Substring(strUitvoer.LastIndexOf("\\") + 1)) { Foreground = Brushes.DarkBlue });

                    if (!isDecryptie)
                    {
                        createImages();
                        btnImages.Visibility = Visibility.Visible;
                    }
                }
            }
        }

        private void tmrElapsed_Tick(object sender, EventArgs e)
        {
            intTijd++;
        }

        #endregion

        #region ### Disable/Enable controls & FinishTriggers ###

        private void disableControls()
        {
            txtLocatieFoto.IsEnabled = false;
            txtSleutel.IsEnabled = false;
            txtGeheimBestand.IsEnabled = false;
            btnBladerenFoto.IsEnabled = false;
            btnBladerenGeheimBestand.IsEnabled = false;
            btnEncrypteren.IsEnabled = false;
            btnDecrypteren.IsEnabled = false;
            btnImages.Visibility = Visibility.Hidden;
        }

        private void enableControls()
        {
            txtLocatieFoto.IsEnabled = true;
            txtSleutel.IsEnabled = true;
            txtGeheimBestand.IsEnabled = true;
            btnBladerenFoto.IsEnabled = true;
            btnBladerenGeheimBestand.IsEnabled = true;
            btnEncrypteren.IsEnabled = true;
            btnDecrypteren.IsEnabled = true;
            btnImages.Visibility = Visibility.Hidden;
        }

        private void createImages()
        {
            // Foto's laden
            BitmapImage biOrigineel = new BitmapImage(new Uri(strLocatieFoto));
            BitmapImage biVersleuteld = new BitmapImage(new Uri(strUitvoer));

            int breedte = (int) biOrigineel.Width;
            int hoogte = (int) biOrigineel.Height;

            int lengteLijn = biOrigineel.PixelWidth * 4;
            int hoogteLijn = biOrigineel.PixelHeight * lengteLijn;
            byte[] pixelsFoto1 = new byte[hoogteLijn];
            biOrigineel.CopyPixels(pixelsFoto1, lengteLijn, 0);

            int lengteLijn2 = biVersleuteld.PixelWidth * 4;
            int hoogteLijn2 = biVersleuteld.PixelHeight * lengteLijn2;
            byte[] pixelsFoto2 = new byte[hoogteLijn2];
            biVersleuteld.CopyPixels(pixelsFoto2, lengteLijn2, 0);

            // WriteableBitmap aanmaken voor de nieuwe foto
            bmVerschil = new WriteableBitmap(breedte, hoogte, biOrigineel.DpiX, biOrigineel.DpiY, biOrigineel.Format, biOrigineel.Palette);

            // Aray maken waar de waardes voor de nieuwe foto inkomen
            uint[] pixels = new uint[breedte * hoogte];

            int red;
            int green;
            int blue;
            bmVerschil.Lock();

            for (int x = 0; x < breedte; x++)
            {
                for (int y = 0; y < hoogte; y++)
                {
                    int index = y * lengteLijn + 4 * x;
                    int total1 = pixelsFoto1[index] + pixelsFoto1[index + 1] + pixelsFoto1[index + 2];
                    int total2 = pixelsFoto2[index] + pixelsFoto2[index + 1] + pixelsFoto2[index + 2];
                    int i = breedte * y + x;

                    // Als de rbg waardes even zijn dan bevatten deze geen data
                    int afgerond = ((int)(pixelsFoto2[index] / 2)) * 2;
                    int origineel = pixelsFoto2[index];
                    int afgerond1 = ((int)(pixelsFoto2[index + 1] / 2)) * 2;
                    int origineel1 = pixelsFoto2[index + 1];
                    int afgerond2 = ((int)(pixelsFoto2[index + 2] / 2)) * 2;
                    int origineel2 = pixelsFoto2[index + 2];
                    int totaalAfgerond = afgerond2 + afgerond1 + afgerond;
                    int totaalOrigineel = origineel2 + origineel1 + origineel;

                    // Als de pixel geen data bevat worden de rgb waardes van de originele foto overgenomen
                    // Wanneer de pixel wel data bevat wordt deze rood gekleurd
                    if (totaalAfgerond == totaalOrigineel)
                    {
                        red = pixelsFoto1[index + 2];
                        green = pixelsFoto1[index + 1];
                        blue = pixelsFoto1[index];
                        pixels[i] = (uint)((red << 16) + (green << 8) + blue);
                    }
                    else
                    {
                        red = 255;
                        green = 0;
                        blue = 0;
                        pixels[i] = (uint)((red << 16) + (green << 8) + blue);
                    }
                }
            }

            // Pixel array wegschrijven naar de bitmap
            bmVerschil.WritePixels(new Int32Rect(0, 0, breedte, hoogte), pixels, breedte * 4, 0);
            bmVerschil.Unlock();
        }

        #endregion

        #region ### Controle Methodes ###

        private bool controleerSleutel()
        {
            // Eén ascii = 8 bits lang. 24 tekens * 8 =192bits (= sleutel lengte)
            if (txtSleutel.Text.Length == 24)
            {
                return true;
            }
            else
            {
                MessageBox.Show("De sleutel moet 24 tekens lang zijn.", "Foutieve sleutel", MessageBoxButton.OK, MessageBoxImage.Exclamation);
                return false;
            }
        }

        private bool controleerBestand()
        {
            if (txtLocatieFoto.Text == "" || txtGeheimBestand.Text == "")
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        private bool controleerBestandEnkel()
        {
            if (txtLocatieFoto.Text == "")
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        private void txtSleutel_KeyDown(object sender, KeyEventArgs e)
        {
            if (txtSleutel.Text.Length < 24)
            {
                e.Handled = false;
            }
            else if (e.Key == Key.Return)
            {
                e.Handled = false;
            }
            else
            {
                e.Handled = true;
            }
        }

        #endregion

        private void Window_Closing_1(object sender, CancelEventArgs e)
        {
            Start window = new Start();
            window.Show();
        }

        private void btnImages_Click(object sender, RoutedEventArgs e)
        {
            if (! Detail.IsOpen)
            {
                detail = new Detail(new BitmapImage(new Uri(strLocatieFoto)), new BitmapImage(new Uri(strUitvoer)), bmVerschil);
                detail.Show();
            }
        }
    }
}
